import { _decorator, Component, Game, instantiate, Label, Node, Prefab, tween, UIOpacity, UITransform, v3, Vec3 } from 'cc';
import { GameManager } from './GameManager';
import { PalBattleEnemy, PalBattleEventEnum, PalBattleHero, PalBattleSystem } from './PalBattleSystem';
import { EnemyItem } from './EnemyItem';
import { HeroItem } from './HeroItem';
import { config } from './Setting';
import { Dialog } from './Dialog';
import { SkillDialog } from '../resources/prefabs/ui/SkillDialog/SkillDialog';
const { ccclass, property } = _decorator;

@ccclass('UiManager')
export class UiManager extends Component {
    // 单例模式
    public static instance = null;
    public static getInstance<UiManager>(): UiManager {
        if (!this.instance) this.instance = new this() as UiManager;
        return this.instance;
    }
    installSingletion() {
        if (!UiManager.instance) {
            UiManager.instance = this;
        }
    }
    protected onLoad(): void {
        this.installSingletion();
    }

    @property(Node)
    public enemyContentNode: Node = null;
    @property(Node)
    public enemyItemNode: Node = null;
    @property(Node)
    public heroContentNode: Node = null;
    @property(Node)
    public heroItemNode: Node = null;
    @property(Node)
    public playerControlNode: Node = null;
    @property(Node)
    public currentHeroNode: Node = null;

    @property(Node)
    public uiIconNode: Node = null;
    @property(Node)
    public uiDialogNode: Node = null;

    @property(Prefab)
    public bulletPrefab: Prefab = null;
    @property(Prefab)
    public damagePrefab: Prefab = null;

    @property(Prefab)
    public skillDialogPrefab: Prefab = null;

    protected start(): void {
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.PushEnemy, this.onPushEnemy, this);
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.PushHero, this.onPushHero, this);
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.TurnChange, this.onTurnChange, this);
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.ChangeHero, this.onChangeHero, this);
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.HitEnemy, this.onHitEnemy, this);
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.HitHero, this.onHitHero, this);
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.EnemyDead, this.onEnemyDead, this);
        (GameManager.instance as GameManager).palBattleSystem.on(PalBattleEventEnum.HeroDead, this.onHeroDead, this);
    }
    protected onDestroy(): void {
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.PushEnemy, this.onPushEnemy, this);
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.PushHero, this.onPushHero, this);
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.TurnChange, this.onTurnChange, this);
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.ChangeHero, this.onChangeHero, this);
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.HitEnemy, this.onHitEnemy, this);
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.HitHero, this.onHitHero, this);
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.EnemyDead, this.onEnemyDead, this);
        (GameManager.instance as GameManager).palBattleSystem.off(PalBattleEventEnum.HeroDead, this.onHeroDead, this);
    }

    getEnemyNode(enemyUUID: string) {
        return this.enemyContentNode.children.find(child => {
            return child.getComponent(EnemyItem).data.saveData.UUID.toString() === enemyUUID;
        });
    }
    getHeroNode(heroUUID: string) {
        return this.heroContentNode.children.find(child => {
            return child.getComponent(HeroItem).data.saveData.UUID.toString() === heroUUID;
        });
    }

    setEnemyPosition() {
        this.enemyContentNode.children.forEach((node, index) => {
            const transform = node.getComponent(UITransform) as UITransform;
            const position = v3(index * (transform.contentSize.width + 10), 0, 0);
            tween(node)
                .to(0.1, { position })
                .start();
        })
    }
    setHeroPosition() {
        this.heroContentNode.children.forEach((node, index) => {
            const transform = node.getComponent(UITransform) as UITransform;
            const position = v3(index * (transform.contentSize.width + 10), 0, 0);
            tween(node)
                .to(0.1, { position })
                .start();
        })
    }

    onPushEnemy(args: { enemy: PalBattleEnemy }) {
        if (this.getEnemyNode(args.enemy.saveData.UUID)) {
            return;
        }
        const enemyNode = instantiate(this.enemyItemNode);
        enemyNode.setParent(this.enemyContentNode);
        enemyNode.active = true;
        const component = enemyNode.getComponent(EnemyItem);
        component.setData(args.enemy);
        component.render();
        this.setEnemyPosition();
    }
    onPushHero(args: { hero: PalBattleHero }) {
        if (this.getHeroNode(args.hero.saveData.UUID)) {
            return;
        }
        const heroNode = instantiate(this.heroItemNode);
        heroNode.setParent(this.heroContentNode);
        heroNode.active = true;
        const component = heroNode.getComponent(HeroItem);
        component.setData(args.hero);
        component.render();
        this.setHeroPosition();
    }
    onTurnChange(args: { turn: 'player' | 'enemy' }) {
        this.currentHeroNode.active = args.turn === 'player';
        if (args.turn === 'player') {
            tween(this.playerControlNode)
                .to(0.1, { scale: v3(1, 1, 1) })
                .start();
        } else {
            tween(this.playerControlNode)
                .to(0.1, { scale: v3(0, 0, 0) })
                .start();
        }
    }
    onChangeHero(args: { hero: PalBattleHero }) {
        if (!args?.hero) return;
        const heroNode = this.getHeroNode(args.hero.saveData.UUID);
        if (!heroNode) return;
        const worldPosition = heroNode.getWorldPosition();
        const heroNodeTransform = heroNode.getComponent(UITransform) as UITransform;
        tween(this.currentHeroNode)
            .to(0.1, {
                worldPosition: v3(
                    worldPosition.x,
                    worldPosition.y + heroNodeTransform.contentSize.height * (1 - heroNodeTransform.anchorY),
                    0)
            })
            .start();
    }
    onHitEnemy(args: { UUID: string, atk: number }) {
        const enemyNode = this.getEnemyNode(args.UUID);
        if (enemyNode) {
            const component = enemyNode.getComponent(EnemyItem);
            component.render();
        }
    }
    onHitHero(args: { UUID: string, atk: number }) {
        const heroNode = this.getHeroNode(args.UUID);
        if (heroNode) {
            const component = heroNode.getComponent(HeroItem);
            component.render();
        }
    }
    onEnemyDead(args: { UUID: string }) {
        const enemyNode = this.getEnemyNode(args.UUID);
    }
    onHeroDead(args: { UUID: string }) {
        const heroNode = this.getHeroNode(args.UUID);
    }

    bullet(fromWorldPosition: Vec3, toWorldPositions: Vec3[]) {
        const bulletNode = instantiate(this.bulletPrefab);
        bulletNode.setParent(this.uiIconNode);
        bulletNode.active = true;
        bulletNode.setWorldPosition(fromWorldPosition);
        toWorldPositions.forEach((toWorldPosition, index) => {
            tween(bulletNode)
                .to(config.bulletSpeed, {
                    worldPosition: v3(
                        toWorldPosition.x,
                        toWorldPosition.y
                        , 0
                    )
                })
                .start();
        })
        tween(bulletNode)
    }
    damage(worldPosition: Vec3, damage: number) {
        const damageNode = instantiate(this.damagePrefab);
        damageNode.getChildByName('Label').getComponent(Label).string = damage.toString();
        damageNode.setParent(this.uiIconNode);
        damageNode.active = true;
        damageNode.setWorldPosition(worldPosition);
        tween(damageNode.getComponent(UIOpacity) as UIOpacity)
            .to(config.damageDelay, { opacity: 255 })
            .delay(1)
            .call(() => {
                damageNode.destroy();
            })
            .start();
    }

    heroAttack(fromUUID: string, toWorldPosition: Vec3) {
        const fromNode = this.getHeroNode(fromUUID);
        if (!fromNode) return;
        const fromWorldPosition = fromNode.getWorldPosition();
        tween(fromNode)
            .to(0.1, { worldPosition: toWorldPosition })
            .to(0.1, { worldPosition: fromWorldPosition })
            .start();
    }
    enemyAttack(fromUUID: string, toWorldPosition: Vec3) {
        const fromNode = this.getEnemyNode(fromUUID);
        if (!fromNode) return;
        const fromWorldPosition = fromNode.getWorldPosition();
        tween(fromNode)
            .to(0.1, { worldPosition: toWorldPosition })
            .to(0.1, { worldPosition: fromWorldPosition })
            .start();
    }
    heroStatusChange(UUID: string, status: string, value: any) {
        const heroNode = this.getHeroNode(UUID);
        if (!heroNode) return;
        const component = heroNode.getComponent(HeroItem);
        component.heroStatusChange({ UUID, status, value });
    }
    enemyStatusChange(UUID: string, status: string, value: any) {
        const enemyNode = this.getEnemyNode(UUID);
        if (!enemyNode) return;
        const component = enemyNode.getComponent(EnemyItem);
        component.enemyStatusChange({ UUID, status, value });
    }
    heroDead(UUID: string) {
        const heroNode = this.getHeroNode(UUID);
        if (!heroNode) return;
        const component = heroNode.getComponent(HeroItem);
        component.heroDead();
    }
    enemyDead(UUID: string) {
        const enemyNode = this.getEnemyNode(UUID);
        if (!enemyNode) return;
        const component = enemyNode.getComponent(EnemyItem);
        component.enemyDead();
    }

    skillDialog(heroUUID: string) {
        const dialogNode = instantiate(this.skillDialogPrefab);
        dialogNode.setParent(this.uiDialogNode);
        dialogNode.getComponent(Dialog).open();
        const component = dialogNode.getComponent(SkillDialog);
        component.setData({ heroUUID });
        component.render();
    }
}


